﻿using System;
using System.Collections.Generic;

namespace Aeon.Framework.Objects.Models.ModelBVH
{

/// <summary>
/// Transfer object class with animation info for BVH files.
/// 
/// </summary>
public sealed class DataBVH
    {
    // --- Fields:

    public Joint Root { get; set; }         // Structure from the file.
    public Motion Motion { get; set; }      // Animation from the file.

    // --- Constructor:

    public DataBVH()
        {
        }

    // --- Internal Methods:

    /// <summary>
    /// Recursively traverse the structure hierarchy and display Joints. 
    /// 
    /// </summary>
    private string display(Joint root, int level)
        {
        String text = String.Empty;

        for(short lvl=0; lvl<level; lvl++) { text += "  "; }
        text += root.ToString()+"\r\n";

        for(int i=0; i<root.Children; i++)
            {
            Joint child = root.getChild(i);
            text += display(child, level+1);
            }

        return(text);
        }

    // --- Public Methods:

    public override string ToString()
        {
        String structure = "*** STRUCTURE\r\n\r\n" + display(Root,0);
        String animation = "*** MOVEMENT\r\n\r\n" + Motion.ToString();

        return(structure+"\r\n"+animation+"\r\n");
        }

    }

/// <summary>
/// BVH movement information class.
/// 
/// </summary>
public sealed class Motion 
    {
    // ---  Fields:

    private double time;          // Frame duration, in seconds.
    private ushort frames;        // Total number of frames.
    private ushort channels;      // Total channels per frame.
    private double[,] data;       // Motion data for each [frame][channel].

    // --- Properties:

    public ushort Channels { get { return(channels); } }
    public ushort Frames { get { return(frames); } }
    public double Time { get { return(time); } }

    // --- Constructor:

    public Motion(double frameTime, ushort frameCount, ushort channelCount)
        {
        time = frameTime;
        frames = frameCount;
        channels = channelCount;
        
        data = new double[frameCount,channelCount];
        }

    // --- Data:

    /// <summary>
    /// Sets the full motion data.
    /// 
    /// </summary>
    public void createData(double[,] motion)
        {
        if ((motion.Rank != 2) || ((ushort)motion.GetLongLength(0) != frames) || ((ushort)motion.GetLongLength(1) != channels))
            {
            throw new ArgumentException("Motion data structure invalid.");
            }
        
        data = motion;
        }

    /// <summary>
    /// Gets data from a specific frame channel.
    /// 
    /// </summary>
    public float getData(ushort frame, ushort channel)
        {
        if ((frame >= frames) || (channel >= channels))
            {
            throw new ArgumentException("Motion frame or channel index out of range.");
            }

        else return((float)data[frame,channel]);    // [TODO] Conversion necessary?
        }

    // --- External:

    public override string ToString()
        {
        String display = String.Empty;

        display += "Frames: " + frames + "\r\n";
        display += "Channels: " + channels + "\r\n";
        display += "Duration: " + (frames*time).ToString() + "\r\n";

        //for(short x = 0; x<frames; x++)
        //    {
        //    for(short y=0;y<channels; y++)
        //        {
        //        display += " (" + data[x,y].ToString() +") ";
        //        }
        //    display += "\r\n";
        //    }

        return(display);
        }
    }
        
/// <summary>
/// BVH skeleton information class.
/// 
/// </summary>
public sealed class Joint
    {
    // --- Fields:

    private String name;                // Joint name.

    private Position position;          // Position coordinates.
    private List<Channel> channels;     // Animated parameters.

    private Joint parent;               // Upper joint root.
    private List<Joint> children;       // Lower joint branches.

    // --- Properties:

    public Position Position { get { return(position); } set { position = value; } }

    public String Name { get { return(name); } }
    public Joint Parent { get { return(parent); } }

    public Int32 Channels { get { return(channels.Count); } }
    public Int32 Children { get { return(children.Count); } }
 
    public Boolean Root { get { return(parent == null); } }
    public Boolean Leaf { get { return(children.Count == 0); } }

    // --- Constructor:

    public Joint(String name, Joint parent) 
        {
        this.name = name;
        this.parent = parent;

        children = new List<Joint>();
        channels = new List<Channel>();

        position = Position.Origin;
        }

    // --- Channels:

    /// <summary>
    /// Adds channel to joint.
    /// 
    /// </summary>
    public void addChannel(Channel channel)
        {
        
        channels.Add(channel);
        }

    /// <summary>
    /// Gets channel at indexed position.
    /// Can throw exceptions.
    /// 
    /// </summary>
    public Channel getChannel(Int32 position)
        {
        if(position < 0 || position >= channels.Count)
            {
            throw new ArgumentException("Channel index out of range.");
            }

        else return(channels[position]);
        }

    /// <summary>
    /// Clears joint of all channels.
    /// 
    /// </summary>
    public void clearChannels()
        {

        channels.Clear();
        }

    // --- Children:

    /// <summary>
    /// Adds child to joint.
    /// 
    /// </summary>
    public void addChild(Joint child) 
        {
        
        children.Add(child);
        }

    /// <summary>
    /// Gets joint child at indexed position.
    /// Can throw exceptions.
    /// 
    /// </summary>
    public Joint getChild(Int32 position)
        {
        if(position < 0 || position >= children.Count)
            {
            throw new ArgumentException("Joint child index out of range.");
            }

        else return(children[position]);    // [TODO] Deep copy for class immutability?
        }

    // --- Methods:

    public override string ToString()
        {
        
        return("Joint: "+name+"  ("+position.xPos+") ("+position.yPos+") ("+position.zPos)+")";
        }

    }

/// <summary>
/// BVH Channel types.
/// 
/// </summary>
public enum Channels : byte
        {
        Xpos = 0,
        Ypos = 1,
        Zpos = 2,
        Xrot = 3,
        Yrot = 4,
        Zrot = 5,

        Err = 6
        }

/// <summary>
/// BVH Channel object.
/// 
/// </summary>
public struct Channel
    {
    private Channels type;
    public Channels Type { get { return(type); } }

    public Channel(String channel)
        {
        switch(channel)
            {
            case("Xposition"): type = Channels.Xpos; break;
            case("Yposition"): type = Channels.Ypos; break;
            case("Zposition"): type = Channels.Zpos; break;
            case("Xrotation"): type = Channels.Xrot; break;
            case("Yrotation"): type = Channels.Yrot; break;
            case("Zrotation"): type = Channels.Zrot; break;

            default: type = Channels.Err; break;
            }
        }
    }


}
